from wordcloud import WordCloud
import  numpy
from PIL import Image
import jieba
import sys
from PyQt5 import QtCore
from PyQt5.QtWidgets import QLabel,QFrame,QFileDialog,QMenu,QPushButton,QAction,QMessageBox,QWidget,QApplication
from PyQt5.QtCore import QThread,pyqtSignal,pyqtProperty
from PyQt5.QtGui import QColor
from PyQt5.Qt import *
import os
import time
import shutil
class Th(QThread):
    end = pyqtSignal()
    Blink = pyqtSignal()
    erro = pyqtSignal()
    def __init__(self,ui):
        super().__init__()
        self.ui = ui
    def run(self):
        self.ui.wordcloud_txt = self.ui.text_endit.toPlainText()
        img_size = (self.ui.img_width.value(), self.ui.img_height.value())
        shape = None
        if self.ui.shape_img_path != '':
            img = Image.open(self.ui.shape_img_path)
            if img.size != img_size:
                img = img.resize((img_size))
            # print(img.size)
            shape = numpy.array(img)
        if self.ui.wordcloud_txt.replace(' ', '') != '':
            word = jieba.cut(self.ui.wordcloud_txt, cut_all=True)
            text = ' '.join(word)
        else:
            self.Blink.emit()
            self.end.emit()
            return
        if self.ui.img_background_color == None:
            mode = 'RGBA'
        else:
            mode = 'RGB'
        try:
            wordcloud = WordCloud(
                width=img_size[0],
                height=img_size[1],
                background_color=self.ui.img_background_color,
                scale=self.ui.scale.value(),
                mask=shape,
                mode=mode,
                font_path=self.ui.img_font,
            ).generate(text)
        except:
            self.erro.emit()
            self.end.emit()
            return
        if self.ui.img_background_color == None:
            wordcloud.to_file('img/temp.png')
            self.ui.show_img_path = 'img/temp.png'
        else:
            wordcloud.to_file('img/temp.jpg')
            self.ui.show_img_path = 'img/temp.jpg'
        self.ui.show_img_width, self.ui.show_img_height = img_size
        self.ui.zoom_num = 0
        self.end.emit()
class BtnLabel(QLabel):
    Enter = pyqtSignal(object)
    Leave = pyqtSignal(object)
    Press_left = pyqtSignal(object,object)
    Press_right = pyqtSignal(object, object)
    Enter = pyqtSignal(object)
    Leave = pyqtSignal(object)
    Doubel_click_left = pyqtSignal(object)
    wheel = pyqtSignal(object, bool)
    move = pyqtSignal(object, object)
    def mousePressEvent(self, evt):
        if evt.buttons() == Qt.LeftButton:
            self.Press_left.emit(self,evt)
        if evt.buttons() == Qt.RightButton:
            self.Press_right.emit(self, evt)
    def mouseDoubleClickEvent(self,evt):
        if evt.buttons() == Qt.LeftButton:
            self.Doubel_click_left.emit(self)
    def enterEvent(self, *args, **kwargs):
        self.Enter.emit(self)
    def leaveEvent(self, *args, **kwargs):
        self.Leave.emit(self)
    def wheelEvent(self, evt):
        # print(False if (evt.angleDelta().y()>0) else True )
        self.wheel.emit(self, False if (evt.angleDelta().y() > 0) else True)
    def mouseMoveEvent(self, evt):
        if evt.buttons() == QtCore.Qt.LeftButton:
            self.move.emit(self,evt)
    def _set_color(self, col):
        self.setAutoFillBackground(True)
        palette = self.palette()
        palette.setColor(self.backgroundRole(), col)
        self.setPalette(palette)
    color = pyqtProperty(QColor, fset=_set_color)
class UI(QWidget):
    def __init__(self):
        super(UI, self).__init__()
        self.setFixedSize(800,600)
        self.setWindowTitle('词语图')
        self.main_ui()
        self.tool_btn()
        self.tool_btn_event()
        self.text_enditor_ui()
        self.choose_shape_ui()
    def main_ui(self):
        self.show_img_path = 'img/wordcloud.png'
        self.show_img = BtnLabel(self)
        self.show_img.setObjectName('show_img')
        self.show_img.setGeometry(0,0,698,600)
        self.show_img.setAlignment(Qt.AlignCenter)
        # self.show_img.setStyleSheet('QLabel#show_img{background-color:white}')
        self.show_img_width = 550
        self.show_img_height = 550
        self.show_img.setPixmap(QPixmap(self.show_img_path).scaled(self.show_img_width,self.show_img_height))
        self.zoom_num = 0
        def change_image_size(evt,value):
            if value:
                if self.zoom_num < 20:
                    self.show_img_width += int(self.show_img_width*0.1)
                    self.show_img_height += int(self.show_img_height*0.1)
                    self.zoom_num += 1
            else:
                if self.zoom_num > -10:
                    self.show_img_width -= int(self.show_img_width*0.1)
                    self.show_img_height -= int(self.show_img_height*0.1)
                    self.zoom_num -= 1
            self.show_img.setPixmap(QPixmap(self.show_img_path).scaled(self.show_img_width, self.show_img_height))
        self.show_img.wheel.connect(change_image_size)
        # 图片显示区域右键菜单
        self.show_img_context_menu = QMenu(self.show_img)
        action_6 = QAction('另存为', self.show_img_context_menu)
        def save_img():
            try:
                file_name = time.strftime("%Y%m%d%H%M%S", time.localtime())
                if os.path.splitext(self.show_img_path)[1] == '.jpg':
                    save_path= QFileDialog.getSaveFileName(self, '保存文件', './' + file_name + '.jpg', 'wordcloud (Img File (*jpg))')[0]
                else:
                    save_path = QFileDialog.getSaveFileName(self, '保存文件', './' + file_name + '.png', 'wordcloud (Img File (*png))')[0]
                # print(save_path)
                if save_path == '':
                    QMessageBox.warning(self, "提示", "取消保存！")
                else:
                    shutil.copyfile(self.show_img_path, os.path.splitext(save_path)[0] + os.path.splitext(self.show_img_path)[1])
                    QMessageBox.information(self, "提示", "保存成功！")
            except:
                QMessageBox.warning(self, "提示", "保存失败！")
        action_6.triggered.connect(save_img)
        self.show_img_context_menu.addAction(action_6)
        def show_context_menu(moudel, evt):
            self.show_img_context_menu.exec_(evt.globalPos())
        self.show_img.Press_right.connect(show_context_menu)
        #等待提示
        self.wait_tips = BtnLabel('请等待...',self)
        self.wait_tips.setGeometry(0,0,800,600)
        self.wait_tips.hide()
        self.wait_tips.setObjectName('wait_tips')
        self.wait_tips._set_color(QColor(130,130,130,150))
        self.wait_gif = QMovie('img/wait.gif')
        self.wait_tips.setMovie(self.wait_gif)
        self.wait_tips.setAlignment(Qt.AlignCenter)

    def tool_btn(self):
        self.right_frame = QFrame(self)
        self.right_frame.setObjectName('left_frame')
        self.right_frame.setGeometry(698, 0, 102, 600)
        self.right_frame.setStyleSheet('QFrame#left_frame{border:0px;border-left:2px solid #7A7A7A}')
        #添加遮罩
        self.shape_btn = QPushButton('添加遮罩', self.right_frame)
        self.shape_btn.setGeometry(2, 0, 100, 25)
        menu = QMenu(self.shape_btn)
        self.action_1 = QAction('选择遮罩', menu)
        menu.addAction(self.action_1)
        self.action_2 = QAction('自定义', menu)
        menu.addAction(self.action_2)
        self.action_5 = QAction('取消遮罩', menu)
        menu.addAction(self.action_5)
        self.shape_btn.setMenu(menu)
        self.shape_img = QLabel(self.right_frame)
        self.shape_img.setObjectName('shape_img')
        self.shape_img.setGeometry(2, 25, 100, 100)
        # self.shape_img.setStyleSheet('QLabel#shape_img{background-color:white;color:red}')
        self.shape_img.setText('添加遮罩\n(非必选,默认全部填充)')
        self.shape_img.setWordWrap(True)
        self.shape_img.setAlignment(Qt.AlignCenter)
        self.shape_img.setScaledContents(True)

        # 添加文本
        self.text_btn = QPushButton('添加文本', self.right_frame)
        self.text_btn.setGeometry(2, 125, 100, 25)
        menu = QMenu(self.text_btn)
        self.action_3 = QAction('选择文件',menu)
        menu.addAction(self.action_3)
        self.action_4 = QAction('编辑文本',menu)
        menu.addAction(self.action_4)
        self.text_btn.setMenu(menu)
        self.text_tips = BtnLabel('请添加文本', self.right_frame)
        self.text_tips.setGeometry(2, 150, 100, 20)
        self.text_tips.setWordWrap(True)
        self.text_tips.setAlignment(Qt.AlignCenter)
        self.text_tips.setObjectName('text_tips')
        self.text_tips.setStyleSheet('QLabel#text_tips{color:red;background-color:white}')
        #背景颜色
        self.img_background_color = None
        def change_color():
            color = QColorDialog.getColor().name()
            self.img_background_color = color
            self.color_show.setStyleSheet('BtnLabel#color_show{background-color:' + color + '}')
            self.color_show.setPixmap(QPixmap(''))
        def set_lucency():
            self.img_background_color = None
            self.color_show.setPixmap(QPixmap('img/rgba.jpg'))
        self.color_choose = QPushButton('设置背景颜色',self.right_frame)
        self.color_choose.setGeometry(2,170,100,25)
        self.color_choose.clicked.connect(change_color)
        self.color_show = BtnLabel(self.right_frame)
        self.color_show.setGeometry(2,195,100,20)
        self.color_show.setObjectName('color_show')
        self.color_show.setAlignment(Qt.AlignCenter)
        self.color_show.setPixmap(QPixmap('img/rgba.jpg'))
        self.color_show.setToolTip('双击恢复透明')
        self.color_show.Doubel_click_left.connect(set_lucency)
        #字体
        self.img_font = 'font/simhei.ttf'
        def change_font():
            font_path = QFileDialog.getOpenFileName(self,'选择字体','./font','Font File (*.ttf)')[0]
            if font_path != '':
                self.img_font = font_path
                self.font_style_show.setText(os.path.basename(self.img_font))
        self.font_style = QPushButton('设置字体',self.right_frame)
        self.font_style.setGeometry(2,215,100,25)
        self.font_style.clicked.connect(change_font)
        self.font_style_show = BtnLabel(os.path.basename(self.img_font),self.right_frame)
        self.font_style_show.setFont(QFont(self.img_font))
        self.font_style_show.setGeometry(0,240,102,20)
        self.font_style_show.setAlignment(Qt.AlignCenter)
        #宽高清晰度
        self.use_img_size_btn = QPushButton('使用图片大小',self.right_frame)
        self.use_img_size_btn.setGeometry(2,260,100,25)
        self.label_1 = QLabel(' 宽 ', self.right_frame)
        self.label_1.setGeometry(2, 290, 30, 20)
        self.label_2 = QLabel(' 高 ', self.right_frame)
        self.label_2.setGeometry(2, 315, 30, 20)
        self.label_3 = QLabel('清晰度', self.right_frame)
        self.label_3.setGeometry(2, 340, 50, 20)
        self.img_width = QSpinBox(self.right_frame)
        self.img_width.setMinimum(0)
        self.img_width.setMaximum(99999)
        self.img_width.setValue(800)
        self.img_width.setGeometry(32, 290, 70, 20)
        self.img_height = QSpinBox(self.right_frame)
        self.img_height.setMinimum(0)
        self.img_height.setMaximum(99999)
        self.img_height.setValue(600)
        self.img_height.setGeometry(32, 315, 70, 20)
        self.scale = QSpinBox(self.right_frame)
        self.scale.setMinimum(1)
        self.scale.setMaximum(20)
        self.scale.setValue(1)
        self.scale.setGeometry(52, 340, 50, 20)

        self.tips = QLabel('     注意\n1.右击图片保存.\n2.必须载入文本.\n3.预览不代表图片最终品质,请保存后查看.\n4.清晰度过高时图片宽高会超出设置.\n5.清晰度数值越高图片越清晰,文件体积增大.', self.right_frame)
        self.tips.setWordWrap(True)
        self.tips.setGeometry(2, 370, 100, 200)
        self.tips.setObjectName('tips')
        self.tips.setStyleSheet('QLabel#tips{color:red;font:13px;background-color:white}')
        self.create = QPushButton('生 成', self.right_frame)
        self.create.setGeometry(2, 570, 100, 30)
    #工具栏事件
    def tool_btn_event(self):
        #打开文本
        def show_text_endit():
            self.text_endit_frame.show()
            self.text_endit_frame.raise_()
        def open_txt():
            file_path = QFileDialog.getOpenFileName(self,'选择文件','','Text File (*.txt)')[0]
            # print(file_path)
            if file_path != '':
                try:
                    with open(file_path, 'r', encoding='utf-8') as fp:
                        txt = fp.read()
                except(UnicodeDecodeError):
                    with open(file_path, 'r', encoding='gbk') as fp:
                        txt = fp.read()
                except:
                    QMessageBox.critical(self,'读取出错','1.程序仅支持utf-8和gbk编码\n2.文件损坏\n3.其它未知错误')
                    return
                # print(txt)
                self.text_endit.setText(txt)
                show_text_endit()
            print(self.wordcloud_txt)
        self.action_3.triggered.connect(open_txt)
        self.action_4.triggered.connect(show_text_endit)
        #添加遮罩图片
        self.shape_img_path = ''
        self.shape_img_moudel = []
        self.shape_moudel_path = {}
        def show_choose_shape():
            def mouse_enter(evt):
                if self.shape_moudel_path[evt] != self.shape_img_path:
                    evt.setStyleSheet('BtnLabel#shape_img_moudel{border:1px solid red}')
            def mouse_leave(evt):
                if self.shape_moudel_path[evt] != self.shape_img_path:
                    evt.setStyleSheet('BtnLabel#shape_img_moudel{border:0px}')
            def mouse_press_left(evt):
                if self.shape_moudel_path[evt] != self.shape_img_path:
                    evt.setStyleSheet('BtnLabel#shape_img_moudel{border:1px solid red}')
                    for item,value in self.shape_moudel_path.items():
                        if value == self.shape_img_path:
                            item.setStyleSheet('BtnLabel#shape_img_moudel{border:0px}')
                            break
                    self.shape_img_path = self.shape_moudel_path[evt]
                    self.shape_img.setPixmap(QPixmap(self.shape_img_path))
            def mouse_press_right(evt):
                if self.shape_moudel_path[evt] == self.shape_img_path:
                    evt.setStyleSheet('BtnLabel#shape_img_moudel{border:0px}')
                    self.shape_img_path = ''
                    self.shape_img.setPixmap(QPixmap(self.shape_img_path))
                    self.shape_img.setText('添加遮罩\n(非必选,默认全部填充)')
            file_list = []
            for file in os.listdir('img'):
                if os.path.splitext(file)[-1] == '.jpg':
                    file_list.append('img/'+file)
            # print(file_list)
            moudel_x = 30
            moudel_y = 25
            for file in file_list:
                moudel = BtnLabel(self.shape_content)
                moudel.setGeometry(moudel_x,moudel_y,100,100)
                if moudel_x == 530:
                    moudel_x = 30
                    moudel_y += 125
                else:
                    moudel_x += 125
                moudel.setPixmap(QPixmap(file))
                moudel.setScaledContents(True)
                moudel.setObjectName('shape_img_moudel')
                self.shape_img_moudel.append(moudel)
                self.shape_moudel_path[moudel] = file
                moudel.Enter.connect(mouse_enter)
                moudel.Leave.connect(mouse_leave)
                moudel.Press_left.connect(mouse_press_left)
                moudel.Press_right.connect(mouse_press_right)

            self.shape_frame.show()
            self.shape_frame.raise_()
        def open_jpg():
            file_path = QFileDialog.getOpenFileName(self,'选择文件','','JPG File (*jpg)')[0]
            if file_path != '':
                self.shape_img.setPixmap(QPixmap(file_path))
                self.shape_img_path = file_path
                self.shape_img.setPixmap(QPixmap(self.shape_img_path))
        self.action_1.triggered.connect(show_choose_shape)
        self.action_2.triggered.connect(open_jpg)
        def delete_shape_img():
            self.shape_img.setPixmap(QPixmap(''))
            self.shape_img_path = ''
            self.shape_img.setText('添加遮罩\n(非必选,默认全部填充)')
        self.action_5.triggered.connect(delete_shape_img)
        #使用图片大小
        def set_img_size():
            if self.shape_img_path != '':
                img =  Image.open(self.shape_img_path)
                self.img_width.setValue(img.size[0])
                self.img_height.setValue(img.size[1])
        self.use_img_size_btn.clicked.connect(set_img_size)
        # 生成词云图
        def blink():
            self.anim = QPropertyAnimation(self.text_tips, b"color")
            self.anim.setDuration(500)
            self.anim.setStartValue(QColor('#f0f0f0'))  # 粉色
            self.anim.setKeyValueAt(0.25, QColor('#ff0000'))  # 红色
            self.anim.setKeyValueAt(0.5, QColor('#f0f0f0'))
            self.anim.setKeyValueAt(0.75, QColor('#ff0000'))
            self.anim.setEndValue(QColor('#f0f0f0'))  # 米黄
            self.anim.start()
        def create_img():
            self.save_btn.animateClick()
            self.shape_frame.hide()
            self.wait_tips.show()
            self.wait_gif.start()
            self.wait_tips.raise_()
            self.th.start()
        def wait_end():
            self.wait_tips.hide()
            self.wait_gif.stop()
        def erro_event():
            QMessageBox.critical(self,'错误:生成图片出错！','错误原因:\n1.导入的字体不支持\n2.清晰度过高,内存不足\n3.其它未知问题')
        # erro_event()
        self.th = Th(self)
        self.th.Blink.connect(blink)
        self.th.end.connect(lambda :self.show_img.setPixmap(QPixmap(self.show_img_path).scaled(self.show_img_width, self.show_img_height)))
        self.th.end.connect(wait_end)
        self.th.erro.connect(erro_event)
        self.create.clicked.connect(create_img)
    #编辑文本界面
    def text_enditor_ui(self):
        self.wordcloud_txt = ''
        self.text_endit_frame = QFrame(self)
        self.text_endit_frame.setObjectName('endit_frame')
        self.text_endit_frame.setGeometry(0,0,698,600)
        self.text_endit_frame.setStyleSheet('QFrame#endit_frame{background-color:#F0F0F0;border:0px;border-top:1px solid black}')
        self.text_endit_frame.hide()

        self.text_endit = QTextEdit(self.text_endit_frame)
        self.text_endit.setGeometry(10,10,678,500)
        def text_tips_show():
            if self.wordcloud_txt == '':
                self.text_tips.setText('请添加文本')
                self.text_tips.setStyleSheet('QLabel#text_tips{color:red}')
            else:
                self.text_tips.setText('文本已添加')
                self.text_tips.setStyleSheet('QLabel#text_tips{color:black}')
            self.text_endit_frame.hide()
        def save_txt():
            self.wordcloud_txt = self.text_endit.toPlainText()
            text_tips_show()
        def off_txt():
            self.text_endit.setText(self.wordcloud_txt)
            text_tips_show()
        self.save_btn = QPushButton('保存',self.text_endit_frame)
        self.save_btn.setGeometry(10,540,300,30)
        self.save_btn.clicked.connect(save_txt)
        self.off_btn_1 = QPushButton('取消',self.text_endit_frame)
        self.off_btn_1.setGeometry(388,540,300,30)
        self.off_btn_1.clicked.connect(off_txt)
    #选择遮罩图片界面
    def choose_shape_ui(self):
        self.shape_file_path = ''
        self.shape_frame = QFrame(self)
        self.shape_frame.setObjectName('shape_frame')
        self.shape_frame.setGeometry(0, 0, 698, 600)
        self.shape_frame.setStyleSheet('QFrame#shape_frame{background-color:#F0F0F0;border:0px;border-top:1px solid black}')
        self.shape_frame.hide()
        self.shape_choose_tips = QLabel('提示:左键选择,右键取消',self.shape_frame)
        self.shape_choose_tips.setGeometry(10,0,300,30)
        self.close_shape_btn = QPushButton('关闭',self.shape_frame)
        self.close_shape_btn.setGeometry(588,0,100,30)
        self.close_shape_btn.clicked.connect(lambda :self.shape_frame.hide())
        self.scroll_area = QScrollArea(self.shape_frame)
        self.scroll_area.setGeometry(10,30,678,560)
        self.shape_content = QFrame(self.scroll_area)
        self.shape_content.resize(660,1000)
        self.shape_content.setObjectName('shape_content')
        # self.shape_content.setStyleSheet('QFrame#shape_content{background-color:red}')
        self.scroll_area.setWidget(self.shape_content)
        self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
App = QApplication(sys.argv)
win = UI()
win.show()
sys.exit(App.exec_())


